# 运行本脚本之前，需要安装一些python库，输入以下命令进行安装
# pip install colorama

import argparse
import ctypes
import os
import re
import subprocess

from colorama import init, Fore, Style

ignore_list = [
    # 原神
    r'YuanShen.exe',
    r'GenshinImpact.exe',
    r'driverError.log',
    r'ScreenShot',
    r'YuanShen_Data\webCaches',
    r'GenshinImpact_Data\webCaches',
    # 星铁
    r'StarRail.exe',
    r'StarRail_Data\webCaches',
    r'StarRail_Data\Persistent\DownloadedFullAssets.txt',
    r'StarRail_Data\Persistent\InstallVersion.bin'
]


def check_user_admin():
    try:
        admin_out = ctypes.windll.shell32.IsUserAnAdmin()
    except:
        print(f'{Fore.RED}无法检测是否以管理员身份运行程序{Style.RESET_ALL}')
        exit(1)

    if not admin_out:
        print(f'{Fore.RED}请以管理员身份运行程序{Style.RESET_ALL}')
        exit(0)
    else:
        print(f'{Fore.GREEN}检测到以管理员身份运行程序{Style.RESET_ALL}')


def check_path(cnpath, ospath, patchpath):
    if not os.path.exists(cnpath) and not os.path.isdir(cnpath):
        print(f'{Fore.RED}未找到国服版本原神资源{Style.RESET_ALL}')
        exit(1)

    if not os.path.exists(patchpath) and not os.path.isdir(patchpath):
        print(f'{Fore.RED}未找到国际服替换资源包{Style.RESET_ALL}')
        exit(1)

    if os.path.exists(ospath):
        if not os.path.isdir(ospath):
            os.remove(ospath)
            os.mkdir(ospath)
    else:
        os.mkdir(ospath)


def run_command(command):
    if command[:6] == 'mklink':
        try:
            # 调用命令行并获取输出结果
            result = subprocess.run(command, shell=True, capture_output=True, text=True)
            # 检查命令是否成功执行
            if result.returncode == 0:
                # 返回输出结果
                return True, result.stdout
            else:
                # 如果命令执行失败，则打印错误信息
                return False, result.stderr
        except Exception as e:
            # 如果发生异常，则打印异常信息
            print("发生异常:")
            print(e)
            exit(1)
    else:
        print(f'无法执行该命令 {Fore.YELLOW}{command}{Style.RESET_ALL}')
        return False, 'error'


def get_directory_contents(path):
    dirnames = set()
    filenames = set()
    for root, dirs, files in os.walk(path):
        for d in dirs:
            dirnames.add(os.path.relpath(os.path.join(root, d), path))
        for f in files:
            filenames.add(os.path.relpath(os.path.join(root, f), path))
    return dirnames, filenames


def compare_directories_recursion(cn_path, os_path):
    dirnames1, filenames1 = get_directory_contents(cn_path)
    dirnames2, filenames2 = get_directory_contents(os_path)

    # 独有文件夹集合
    unique_dirs_cn = dirnames1 - dirnames2
    unique_dirs_os = dirnames2 - dirnames1

    # 独有文件集合
    unique_files_cn = filenames1 - filenames2
    unique_files_os = filenames2 - filenames1

    if len(unique_dirs_cn) == len(unique_dirs_os) and len(unique_files_cn) == len(unique_files_os):
        return True
    else:
        return False


def complex_rule(item, path):
    Regular = r'.:.+\\((GenshinImpact_Data|YuanShen_Data|StarRail_Data).+)'
    Matching_results = re.match(Regular, f'{path}\\{item}')
    if (Matching_results is not None and Matching_results[1] in ignore_list) or (item in ignore_list):
        return False
    return True


def compare_directory_contents_no_recursion(cn_path, os_path):
    # 获取两个路径下的文件夹列表
    dirs1 = set(dir for dir in os.listdir(cn_path) if os.path.isdir(os.path.join(cn_path, dir)))
    dirs2 = set(dir for dir in os.listdir(os_path) if os.path.isdir(os.path.join(os_path, dir)))

    # 剔除不需要链接的文件夹
    dirs1 = {item for item in dirs1 if complex_rule(item, cn_path)}
    dirs2 = {item for item in dirs2 if complex_rule(item, os_path)}

    # 比较文件夹
    common_dirs = dirs1.intersection(dirs2)
    unique_dirs1 = dirs1 - dirs2
    unique_dirs2 = dirs2 - dirs1

    # 获取两个路径下的文件列表
    files1 = set(file for file in os.listdir(cn_path) if os.path.isfile(os.path.join(cn_path, file)))
    files2 = set(file for file in os.listdir(os_path) if os.path.isfile(os.path.join(os_path, file)))

    # 剔除不需要链接的文件
    files1 = {item for item in files1 if complex_rule(item, cn_path)}
    files2 = {item for item in files2 if complex_rule(item, os_path)}

    # 比较文件名
    common_files = files1.intersection(files2)
    unique_files1 = files1 - files2
    unique_files2 = files2 - files1

    return {
        'common_dirs': common_dirs,
        'unique_dirs_in_cn': unique_dirs1,
        'unique_dirs_in_os': unique_dirs2,
        'common_files': common_files,
        'unique_files_in_cn': unique_files1,
        'unique_files_in_os': unique_files2
    }


def run_mklink(file_type, version, link, target):
    Regular = r'.:.+\\((GenshinImpact_Data|YuanShen_Data|StarRail_Data).+)'

    if file_type == 'd':
        path_tip_color = Fore.BLUE
        command_bool, command_result = run_command(f'mklink /D "{link}" "{target}"')
    elif file_type == 'H':
        path_tip_color = ''
        command_bool, command_result = run_command(f'mklink /H "{link}" "{target}"')
    else:
        path_tip_color = ''
        command_bool, command_result = run_command(f'mklink "{link}" "{target}"')

    is_data_folder = lambda path: re.match(Regular, path)[1] if re.match(Regular, path) else os.path.basename(path)
    if command_bool:
        print(f'{Fore.GREEN}连接成功{Style.RESET_ALL} {version}:{path_tip_color}{is_data_folder(target)}{Style.RESET_ALL} -> {path_tip_color}{is_data_folder(link)}')
    else:
        print(f'{Fore.RED}连接失败{Style.RESET_ALL} {version}:{path_tip_color}{is_data_folder(target)}{Style.RESET_ALL} -> {path_tip_color}{is_data_folder(link)} {Fore.RED}{command_result}{Style.RESET_ALL}', end='')


def compare_recursion(ysCN_path, ysOS_path, ysOS_patch_path):
    comparison_result = compare_directory_contents_no_recursion(ysCN_path, ysOS_patch_path)
    # 处理国服独有文件夹
    for d in comparison_result['unique_dirs_in_cn']:
        run_mklink('d', 'CN', f'{ysOS_path}\\{d}', f'{ysCN_path}\\{d}')
    # 处理国服独有文件
    for f in comparison_result['unique_files_in_cn']:
        run_mklink('f', 'CN', f'{ysOS_path}\\{f}', f'{ysCN_path}\\{f}')
    # 处理资源包文件
    for f in comparison_result['common_files'] | comparison_result['unique_files_in_os']:
        run_mklink('f', 'OS', f'{ysOS_path}\\{f}', f'{ysOS_patch_path}\\{f}')
    # 处理共有文件夹
    for d in comparison_result['common_dirs']:
        compare_bool = compare_directories_recursion(f'{ysCN_path}\\{d}', f'{ysOS_patch_path}\\{d}')
        if compare_bool:
            run_mklink('d', 'OS', f'{ysOS_path}\\{d}', f'{ysOS_patch_path}\\{d}')
        else:
            os.makedirs(f'{ysOS_path}\\{d}', exist_ok=True)
            compare_recursion(f'{ysCN_path}\\{d}', f'{ysOS_path}\\{d}', f'{ysOS_patch_path}\\{d}')


def ys_cn2os(ysCN_path, ysOS_path, ysOS_patch_path):
    comparison_result = compare_directory_contents_no_recursion(ysCN_path, ysOS_patch_path)

    # 处理启动程序
    if os.path.exists(f'{ysOS_patch_path}\\GenshinImpact.exe'):
        run_mklink('H', 'OS', f'{ysOS_path}\\GenshinImpact.exe', f'{ysOS_patch_path}\\GenshinImpact.exe')
    # 处理截图文件夹
    if os.path.exists(f'{ysCN_path}\\ScreenShot'):
        run_mklink('d', 'CN', f'{ysOS_path}\\ScreenShot', f'{ysCN_path}\\ScreenShot')
    # 处理国服独有文件
    for f in comparison_result['unique_files_in_cn']:
        run_mklink('f', 'CN', f'{ysOS_path}\\{f}', f'{ysCN_path}\\{f}')
    # 处理资源包文件
    for f in comparison_result['common_files'] | comparison_result['unique_files_in_os']:
        run_mklink('f', 'OS', f'{ysOS_path}\\{f}', f'{ysOS_patch_path}\\{f}')
    # 递归处理
    os.makedirs(f'{ysOS_path}\\GenshinImpact_Data', exist_ok=True)
    compare_recursion(f'{ysCN_path}\\YuanShen_Data', f'{ysOS_path}\\GenshinImpact_Data',
                      f'{ysOS_patch_path}\\GenshinImpact_Data')


def sr_cn2os(CN_path, OS_path, OS_patch_path):
    # 递归处理
    compare_recursion(f'{CN_path}', f'{OS_path}',
                      f'{OS_patch_path}')

    # 处理启动程序
    if os.path.exists(f'{OS_patch_path}\\StarRail.exe'):
        run_mklink('H', 'OS', f'{OS_path}\\StarRail.exe', f'{OS_patch_path}\\StarRail.exe')


def main():
    # 初始化 colorama
    init(autoreset=True)
    check_user_admin()

    # 创建解析器
    parser = argparse.ArgumentParser(description='Process some parameters.')
    # 添加参数
    parser.add_argument('--game', type=str, required=True, help='需要处理的客户端（游戏）类型，如原神（gs）')
    parser.add_argument('--cnpath', type=str, required=True, help='国服客户端的根目录路径')
    parser.add_argument('--ospath', type=str, required=True, help='生成的国际服客户端的根目录路径')
    parser.add_argument('--patchpath', type=str, required=True, help='资源替换包的所在路径')
    # 解析参数
    args = parser.parse_args()

    if args.game == 'gs':
        game_name = '原神'
    elif args.game == 'sr':
        game_name = '崩坏：星穹铁道'
    else:
        game_name = f'{Fore.RED}识别错误{Style.RESET_ALL}'
        print(f'游戏名称：{game_name}')
        exit(1)

    print(f'游戏名称：{game_name}')
    print(f'国服路径：{args.cnpath}')
    print(f'国际服路径：{args.ospath}')
    print(f'资源包路径：{args.patchpath}')

    check_path(args.cnpath, args.ospath, args.patchpath)

    user_confirm = input(f"{Fore.YELLOW}以上信息是否全部正确[Y/n]{Style.RESET_ALL}")
    if user_confirm == "y" or user_confirm == "Y":
        is_empty_folder = lambda path: True if not os.path.exists(path) else True if not os.listdir(path) else False
        if not is_empty_folder(args.ospath):
            print(f'国际服文件夹：{args.ospath} {Fore.RED}为非空文件夹，请重新选择一个空的文件夹{Style.RESET_ALL}')
            exit(0)
        if args.game == 'gs':
            ys_cn2os(args.cnpath, args.ospath, args.patchpath)
        elif args.game == 'sr':
            sr_cn2os(args.cnpath, args.ospath, args.patchpath)
    else:
        print('程序退出，请重新运行命令')
        exit(0)


if __name__ == "__main__":
    main()
